package gitlet;


import javax.swing.JOptionPane;
import java.io.File;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.Serializable;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Scanner;
import java.util.List;
import java.util.Set;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.TreeSet;
import java.util.Arrays;
import java.util.TreeMap;
import java.util.Date;
import java.util.Objects;

import static gitlet.Utils.join;
import static gitlet.Utils.saveHistory;
import static gitlet.Utils.readContentsAsString;
import static gitlet.Utils.writeContents;
import static gitlet.Utils.plainFilenamesIn;
import static gitlet.Utils.readContents;
import static gitlet.Utils.readObject;
import static gitlet.Utils.sha1;
import static gitlet.Utils.restrictedDelete;

/**
 * @CWD = new File(directory); CURRENT_WORK_DIR
 * @GITLET_DIR = join(CWD, ".gitlet");
 * @OBJECTS_DIR = join(GITLET_DIR, "objects");存储对象的目录,本地仓库，git中对象分为三种：commit对象，tree对象(多叉树)，blob对象；
 * @COMMITS_DIR = join(OBJECTS_DIR, "commits");
 * @BLOBS_DIR = join(OBJECTS_DIR, "blobs"); binary large object 每次commit文件文本内容路径
 * @HEAD_FILE = join(GITLET_DIR, "HEAD"); HEAD是当前分支引用的指针，它总是指向某次commit，默认是上一次的commit。 这表示 HEAD 将是下一次提交的父结点。 通常，可以把 HEAD
 * 看做你的上一次提交的快照。当然HEAD的指向是可以改变的，比如你提交了commit，切换了仓库，分支，或者回滚了版本，切换了tag等。
 * @REFS_DIR = join(GITLET_DIR, "refs");refs目录下存储的是仓库和tags，每个仓库下又有分支，每个tags下又有tag，一个tag对应的某次commit。
 * @BRANCH_HEADS_DIR = join(REFS_DIR, "heads");
 * @REMOTE_HEADS_DIR = join(REFS_DIR, "remotes");remotes则是存储最近一次push到远程仓库的commit对象的sha-1值；
 * @INDEX_FILE = join(GITLET_DIR, "index");存储缓冲区(GitExtensions中的stage)的内容，内容包括它指向的文件的时间戳、文件名、sha1值等;(git三大区域:工作区，缓冲区，历史记录区，如下图)
 */
public class Repository implements Serializable {
    private File CWD;
    private File GITLET_DIR;
    private File OBJECTS_DIR;
    private File COMMITS_DIR;
    private File BLOBS_DIR;
    private File HEAD_FILE;
    private File REFS_DIR;
    private File BRANCH_HEADS_DIR;
    private File REMOTE_HEADS_DIR;
    private File INDEX_FILE;
    private String currentCommit;
    private String currentBranchPath;
    private String currentBranch;
    private Stage currentStage;
    private static String PATH;

    /**
     * 构建仓库
     *
     * @param Path 仓库的路径
     * @throws IOException 如果发生I/O错误
     * @author 王子琪、杨凯伟
     * @date 2023/11/16
     */
    public Repository(String Path) throws IOException {
        PATH = Path;
        CWD = new File(Path);
        GITLET_DIR = join(CWD, ".gitlet");

        while (true) {
            if (GITLET_DIR.exists()) {
                System.out.println("当前项目已存在git~~请继续进行操作！");
                break;
            } else if (!CWD.exists()) {
                System.out.println("不存在该项目, 请重新选择！");
                CWD = new File(Utils.chooseFilePath());
                GITLET_DIR = join(CWD, ".gitlet");
            } else {
                OBJECTS_DIR = join(GITLET_DIR, "objects");
                COMMITS_DIR = join(OBJECTS_DIR, "commits");
                BLOBS_DIR = join(OBJECTS_DIR, "blobs");
                HEAD_FILE = join(GITLET_DIR, "HEAD");
                REFS_DIR = join(GITLET_DIR, "refs");
                BRANCH_HEADS_DIR = join(REFS_DIR, "heads");
                REMOTE_HEADS_DIR = join(REFS_DIR, "remotes");
                INDEX_FILE = join(GITLET_DIR, "index");
                this.init();
                break;
            }
        }
        OBJECTS_DIR = join(GITLET_DIR, "objects");
        COMMITS_DIR = join(OBJECTS_DIR, "commits");
        BLOBS_DIR = join(OBJECTS_DIR, "blobs");
        HEAD_FILE = join(GITLET_DIR, "HEAD");
        REFS_DIR = join(GITLET_DIR, "refs");
        BRANCH_HEADS_DIR = join(REFS_DIR, "heads");
        REMOTE_HEADS_DIR = join(REFS_DIR, "remotes");
        INDEX_FILE = join(GITLET_DIR, "index");

        saveHistory(Path);
        currentBranchPath = Utils.readContentsAsString(HEAD_FILE);
        currentBranch = currentBranchPath.split("/")[2]; // refs/heads/master
        File f = join(GITLET_DIR, currentBranchPath);
        currentCommit = readContentsAsString(f);
        currentStage = Stage.fromFile(INDEX_FILE);
    }

    /**
     * 返回GITLET文件夹
     *
     * @return gitlet文件夹
     * @author 王子琪、杨凯伟
     * @date 2023/12/01
     */
    public File getGITLET_DIR() {
        return GITLET_DIR;
    }

    /**
     * 返回分支文件夹
     *
     * @return 返回分支文件夹
     * @author 王子琪、杨凯伟
     * @date 2023/12/01
     */
    public File getBRANCH_HEADS_DIR() {
        return BRANCH_HEADS_DIR;
    }

    /**
     * 返回当前分支
     *
     * @return 返回当前分支
     * @author 王子琪、杨凯伟
     * @date 2023/12/01
     */
    public String getCurrentBranch() {
        return currentBranch;
    }

    /**
     * 返回工作区路径
     *
     * @return 返回工作区路径
     * @author 王子琪、杨凯伟
     * @date 2023/12/01
     */
    public static String getPath() {
        return PATH;
    }

    /**
     * 返回当前暂存区
     *
     * @return 返回当前暂存区
     * @author 王子琪、杨凯伟
     * @date 2023/12/01
     */
    public Stage getCurrenntStage() {
        return currentStage;
    }

    /**
     * 根据remoteUrl添加名为remoteName远程地址
     *
     * @param remoteName 本地连通远程的nickname
     * @param remoteUrl  格式为 "仓库地址/指定项目名.gitlet"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void remoteAdd(String remoteName, String remoteUrl) {
        String s = "";
        File configFile = join(GITLET_DIR, "config");
        if (configFile.exists())
            s = readContentsAsString(configFile);

        if (s.contains("[remote " + remoteName + "]")) {
            System.out.println("已经存在一个同名的远程!");
            // A remote with that name already exists.
            return;
        }

        File remoteGitlet = new File(remoteUrl);
        if (!remoteGitlet.getParentFile().exists()) {
            System.out.println("查找的仓库不存在!");
            return;
        }
        if (!remoteGitlet.exists()) {
            // 远程连接不存在
            File temp_OBJECTS_DIR = join(remoteUrl, "objects");
            File temp_COMMITS_DIR = join(temp_OBJECTS_DIR, "commits");
            File temp_BLOBS_DIR = join(temp_OBJECTS_DIR, "blobs");
            File temp_BRANCH_HEADS_DIR = join(remoteUrl, "heads");
            remoteGitlet.mkdir();
            temp_OBJECTS_DIR.mkdir();
            temp_COMMITS_DIR.mkdir();
            temp_BLOBS_DIR.mkdir();
            temp_BRANCH_HEADS_DIR.mkdir();
            Commit commit = new Commit(temp_COMMITS_DIR, "initial commit", "", "");
            writeContents(join(temp_BRANCH_HEADS_DIR, "master"), commit.getHash());
            commit.saveCommit(temp_COMMITS_DIR);
            // 保存当前commit
            System.out.println("创建远程项目并初始化已完成--远程项目已连接!");
        }

        String sb = s +
                "[remote " + remoteName + "]\n" +
                remoteUrl + "\n";

        writeContents(configFile, sb);
        recordOperation("remoteAdd " + remoteName + " " + remoteUrl);
    }

    /**
     * 添加远程地址
     *
     * @param args 参数形如add-remote [RemoteName] [RemoteURL]
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void remoteAdd(String[] args) {
        if (args.length != 3) {
            System.out.println("参数数量错误!");
            return;
        }
        remoteAdd(args[1], args[2]);
    }

    /**
     * 根据remoteName移除远程地址
     *
     * @param remoteName 远程名称
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void remoteRemove(String remoteName) {
        File configFile = join(GITLET_DIR, "config");
        String s = readContentsAsString(configFile);
        String savedRemoteName = "[remote " + remoteName + "]";
        if (!s.contains(savedRemoteName)) {
            System.out.println("同名远程不存在!");
            return;
        }

        StringBuilder newContent = new StringBuilder();
        try {
            Scanner scanner = new Scanner(configFile);

            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();

                if (line.contains(savedRemoteName)) {
                    for (int i = 0; i < 2; i++) {
                        scanner.nextLine();
                        if (!scanner.hasNextLine()) {
                            break;
                        }
                    }
                } else {
                    // 否则写到当前文件的config中
                    newContent.append(line);
                    newContent.append("\n");
                }
            }
            scanner.close();
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        }
        writeContents(configFile, newContent.toString());
        recordOperation("remoteRemove " + remoteName);
    }

    /**
     * 移除远程地址
     *
     * @param args 参数形如rm-remote [RemoteName]
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void remoteRemove(String[] args) {
        if (args.length != 2) {
            System.out.println("参数数量错误!");
            return;
        }
        remoteRemove(args[1]);
    }

    /**
     * 根据远程名获取远程地址
     *
     * @param remoteName 远程仓库名
     * @return {@link String} 远程仓库地址
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public String getUrlFromRemoteName(String remoteName) {
        File configFile = join(GITLET_DIR, "config");
        if (!configFile.exists())
            return null;
        String line;
        try (BufferedReader reader = new BufferedReader(new FileReader(configFile))) {
            while ((line = reader.readLine()) != null) {
                if (line.contains("[remote " + remoteName + "]")) {
                    line = reader.readLine();
                    return line;
                }
            }
        } catch (IOException er) {
            er.printStackTrace();
        }
        return null;
    }

    /**
     * 把这个remote源的remoteBranch的所有内容都下载下来
     *
     * @param remoteName   远程名称
     * @param remoteBranch 远程分支
     * @throws IOException 如果发生I/O错误
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void fetch(String remoteName, String remoteBranch) throws IOException {
        // 先搬运一些配置文件
        // 判断remote branch是否在remote name这个仓库下存在
        String url = getUrlFromRemoteName(remoteName);
        if (url == null) {
            System.out.printf("不存在%s远程仓库!%n", remoteName);
            return;
        }
        File f1 = new File(url);
        if (!f1.exists()) {
            System.out.println("远程地址不存在!");
            return;
        }

        File f2 = join(url, "heads", remoteBranch);
        if (!f2.exists()) {
            System.out.println("同名远程不存在此分支!");
            return;
        }

        if (!REMOTE_HEADS_DIR.exists()) {
            REMOTE_HEADS_DIR.mkdir();
        }

        File outputDir = join(REMOTE_HEADS_DIR, remoteName);
        if (!outputDir.exists()) {
            outputDir.mkdir();
        }
        String content = readContentsAsString(f2);

        File outputFile = join(outputDir, remoteBranch);
        writeContents(outputFile, content);
        moveBlobs(url, 2);
        moveCommits(url, getRemoteCommits(url, content), 2);
        if (currentBranch.equals(remoteBranch)) {
            if (!changeCommit(currentCommit, content))
                return;
            currentCommit = content;
        }
        File branch = join(BRANCH_HEADS_DIR, remoteBranch);
        if (!branch.exists())
            branch.createNewFile();
        writeContents(branch, content);
        writeContents(HEAD_FILE, "refs/heads/" + remoteBranch);
        recordOperation("fetch " + remoteName + " " + remoteBranch);
    }

    /**
     * 抓取远程分支
     *
     * @param args 参数形如fetch [RemoteName] [RemoteBranch]
     * @throws IOException 如果发生I/O错误
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void fetch(String[] args) throws IOException {
        if (args.length != 3) {
            System.out.println("参数数量错误!");
            return;
        }
        fetch(args[1], args[2]);
    }

    /**
     * 从remote repo的url中获取某一个commit以及其所有的previous commits
     * url不是.gitlet结尾
     *
     * @param url        链接
     * @param remoteHead 远程分支头
     * @return commits 根据远程分支头获取到的提交
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public List<String> getRemoteCommits(String url, String remoteHead) {
        List<String> commits = new ArrayList<>();

        Commit commit = Commit.fromRemoteFile(url, remoteHead);

        while (!commit.getParent().isEmpty()) {
            commits.add(commit.getHash());
            commit = Commit.fromRemoteFile(url, commit.getParent());
        }
        return commits;
    }

    /**
     * step 1 找出remote branch的head，找出当前branch的head，如果没有branch就加这个branch
     * step 2 判断remoteHead是否是branchHead的祖先
     * step 3 如果是的话，就把现在branch超前的commit和commit对应的内容放到remote的objects里面
     * step 4 更新remoteBranch的head内容
     *
     * @param remoteName 远程仓库名
     * @throws IOException 如果发生I/O错误
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void push(String remoteName) throws IOException {
        String remoteBranch = currentBranch;
        // 查找remoteName，获取directory的地址
        if (!hasBranch(remoteBranch, 1, null)) {
            System.out.println("本地无此分支!");
            return;
        }
        if (!currentBranch.equals(remoteBranch)) {
            System.out.println("请先切换到要上传的分支!");
            return;
        }

        String url = getUrlFromRemoteName(remoteName);
        if (url == null) {
            System.out.printf("不存在%s远程仓库!%n", remoteName);
            return;
        }
        File f = new File(url);
        if (!f.exists()) {
            System.out.println("远程地址不存在!");
            return;
        }

        String remoteHead = getRemoteBranchHead(url, remoteBranch);
        if (remoteHead == null)
            System.out.printf("不存在名为：%s 的远程分支，将新建此远程分支！%n", remoteBranch);
        else if (currentCommit.equals(remoteHead)) {
            System.out.println("没有变化!");
            return;
        }

        List<String> futureCommits = isAncestor(currentCommit, remoteHead);
        if (futureCommits == null) {
            System.out.println("在推送前请先拉取远程!");
            return;
        }
        // 所有blob
        moveBlobs(url, 1);
        // 所有(超前的)commit
        moveCommits(url, futureCommits, 1);

        File remoteBranchHead = join(url, "heads", remoteBranch);
        writeContents(remoteBranchHead, currentCommit);
        // 然后根据那个commit把文件都弄进去
        // 根据commit的状态和文件夹的状态
        recordOperation("push " + remoteName + " " + remoteBranch);
    }

    /**
     * 向远程分支推送
     *
     * @param args 参数形如push [RemoteName] [RemoteBranch]
     * @throws IOException 如果发生I/O错误
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void push(String[] args) throws IOException {
        if (args.length != 2) {
            System.out.println("参数数量错误!");
            return;
        }
        push(args[1]);
    }

    /**
     * 把当前文件夹的所有blobs与url对应的blobs文件夹中转移
     *
     * @param url  远程仓库地址
     * @param type 类型(1/转移到远程文件夹；2/转移到本地文件夹)
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void moveBlobs(String url, int type) {
        List<String> blobs;
        if (type == 1) {
            blobs = plainFilenamesIn(BLOBS_DIR);
        } else {
            blobs = plainFilenamesIn(join(url, "objects/blobs"));
        }
        if (blobs != null) {
            for (String blob : blobs) {
                File oldBlob = join(BLOBS_DIR, blob);
                File newBlob = join(url, "objects/blobs", blob);

                if (type == 1 && !newBlob.exists()) {
                    try {
                        Files.copy(oldBlob.toPath(), newBlob.toPath());
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                } else if (type == 2 && !oldBlob.exists()) {
                    try {
                        Files.copy(newBlob.toPath(), oldBlob.toPath());
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

    /**
     * 把当前文件夹的一些commits在url对应的commits文件夹之间转移
     *
     * @param url     远程仓库地址
     * @param commits 提交的列表
     * @param type    转移类型(1/转移到远程文件夹；2/转移到本地文件夹)
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void moveCommits(String url, List<String> commits, int type) {
        for (String commit : commits) {
            File oldCommit = join(COMMITS_DIR, commit);
            File newCommit = join(url, "objects/commits", commit);
            if (type == 1) {
                writeContents(newCommit, readContents(oldCommit));
            } else if (type == 2) {
                writeContents(oldCommit, readContents(newCommit));
            }
        }
    }

    /**
     * 获取远程分支头
     *
     * @param url          远程仓库地址
     * @param remoteBranch 远程分支
     * @return 远程分支头对应的提交
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public String getRemoteBranchHead(String url, String remoteBranch) {
        File f = join(url, "heads", remoteBranch);
        if (!f.exists())
            return null;
        return readContentsAsString(f);
    }

    /**
     * 判断remoteHead是不是curHead的祖先
     * 遍历curHead的所有祖先，看看有没有remoteHead的hash
     *
     * @param curHead    当前分支头
     * @param remoteHead 远程分支头
     * @return futureCommits 超前的所有提交
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public List<String> isAncestor(String curHead, String remoteHead) {
        List<String> futureCommits = new ArrayList<>();
        int length = commitLength(curHead);
        Commit commit1 = Commit.fromFile(COMMITS_DIR, curHead);
        String parent1;
        if (commit1.getParent().isEmpty())
            return futureCommits;
        if (remoteHead == null) {
            for (int i = 0; i < length - 1; i++) {
                futureCommits.add(commit1.getHash());
                parent1 = commit1.getParent();
                commit1 = Commit.fromFile(COMMITS_DIR, parent1);
            }
            return futureCommits;
        }
        for (int i = 0; i < length - 1; i++) {
            if (commit1.getHash().equals(remoteHead))
                break;
            futureCommits.add(commit1.getHash());
            parent1 = commit1.getParent();
            commit1 = Commit.fromFile(COMMITS_DIR, parent1);
        }
        if (remoteHead.equals(commit1.getHash()))
            return futureCommits;
        return null;
    }

    /**
     * 拉取远程分支并合并
     *
     * @param remoteName   远程仓库名
     * @param remoteBranch 远程分支名
     * @throws IOException 如果发生I/O错误
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void pull(String remoteName, String remoteBranch) throws IOException {
        // fetch之后merge
        fetch(remoteName, remoteBranch);
        merge(remoteName + "/" + remoteBranch, 2);
    }

    /**
     * 拉取远程分支并合并
     *
     * @param args 参数形如pull [RemoteName] [RemoteBranch]
     * @throws IOException 如果发生I/O错误
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void pull(String[] args) throws IOException {
        if (args.length != 3) {
            System.out.println("参数数量错误!");
            return;
        }
        pull(args[1], args[2]);
    }

    /**
     * 初始化gitlet仓库
     * 第一步：
     * 如果已经存在GITLET_DIR，输出存在
     * 否则新建
     * 如果不存在OBJECTS_DIR、HEAD_FILE、REFS_DIR、BRANCH_HEADS_DIR、COMMITS_DIR、BLOBS_DIR，就新建对应文件
     * 第二步：
     * 生成初始Commit，把文件位置存放到HEAD_FILE，，同时heads/master文件存放这个commit的SHA1
     *
     * @param cmd 参数应为init
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void init(String[] cmd) {
        System.out.println("当前路径不存在Git,执行初始化操作！");
        if (cmd.length != 1) {
            System.out.println("参数数量错误!");
            return;
        }
        if (GITLET_DIR.exists()) {
            String m;
            m = "当前路径已经存在Git版本控制系统!";
            System.out.println(m);
            return;
        } else {
            GITLET_DIR.mkdir();
        }

        if (!OBJECTS_DIR.exists()) {
            OBJECTS_DIR.mkdir();
        }
        if (!HEAD_FILE.exists()) {
            try {
                HEAD_FILE.createNewFile();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        if (!REFS_DIR.exists()) {
            REFS_DIR.mkdir();
        }
        if (!BRANCH_HEADS_DIR.exists()) {
            BRANCH_HEADS_DIR.mkdir();
        }
        if (!COMMITS_DIR.exists()) {
            COMMITS_DIR.mkdir();
        }
        if (!BLOBS_DIR.exists()) {
            BLOBS_DIR.mkdir();
        }
        // 生成初始的commit，双亲ID为空
        Commit commit = new Commit(COMMITS_DIR, "initial commit", "", "");
        // HEAD文件里面存放这个初始commit的文件位置
        Utils.writeContents(HEAD_FILE, "refs/heads/master");
        // heads/master文件存放这个commit的SHA1
        writeRefs("master", commit.getHash());
        commit.saveCommit(COMMITS_DIR); // 保存当前commit
        currentStage = new Stage();
        currentStage.saveStage(INDEX_FILE);
    }

    /**
     * 初始化gitlet仓库
     *
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void init() {
        init(new String[]{"init"});
    }

    /**
     * 在BRANCH_HEADS_DIR+branch文件里写入commitID(SHA1)
     *
     * @param branch   分支名
     * @param commitID 提交的ID
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void writeRefs(String branch, String commitID) {
        writeContents(join(BRANCH_HEADS_DIR, branch), commitID);
    }

    /**
     * 输出当前commit和所有的parent commit
     *
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void log() {
        Commit commit = Commit.fromFile(COMMITS_DIR, currentCommit);
        System.out.println(commit);
        String curCommit = commit.getParent();
        while (!curCommit.isEmpty()) {
            commit = Commit.fromFile(COMMITS_DIR, curCommit);
            System.out.println(commit);
            curCommit = commit.getParent();
        }
    }

    /**
     * 输出当前commit和所有的parent commit
     *
     * @param cmd 参数应为"log"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void log(String[] cmd) {
        if (cmd.length != 1) {
            System.out.println("参数数量错误!");
            return;
        }
        log();
    }

    /**
     * 按照字典序输出所有的commit信息
     * plainFilenamesIn(COMMITS_DIR)返回字典序Commit_SHA1
     *
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void globalLog() {
        List<String> commits = plainFilenamesIn(COMMITS_DIR);
        if (commits != null) {
            for (String id : commits) {
                System.out.println(readObject(join(COMMITS_DIR, id), Commit.class));
            }
        }
    }

    /**
     * 按照字典序输出所有的commit信息
     *
     * @param args 参数应为"globalLog"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void globalLog(String[] args) {
        if (args.length > 1) {
            System.out.println("参数数量错误!");
            return;
        }
        globalLog();
    }

    /**
     * 找到和message匹配的commits
     * 字典序排列Commit_SHA1,通过遍历COMMITS_DIR+commitSHA1文件
     * 如果没找到，输出Found no commit with that message.
     *
     * @param message 提交时附带的信息
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void find(String message) {
        List<String> commits = plainFilenamesIn(COMMITS_DIR);
        int count = 0;
        if (commits != null) {
            for (String id : commits) {
                File file = join(COMMITS_DIR, id);
                Commit commit = readObject(file, Commit.class);
                if (message.equals(commit.getMessage())) { // 注意不能用==
                    System.out.println(id);
                    count++;
                }
            }
        }
        if (count <= 0)
            System.out.println("没找到与此信息相关的提交！");
    }

    /**
     * 找到和message匹配的commits
     *
     * @param args 参数应为"find [Message]"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void find(String[] args) {
        if (args.length > 2) {
            System.out.println("参数数量错误！");
            return;
        }
        find(args[1]);
    }

    /**
     * 根据参数个数 调用不同checkout函数
     * 三种情况
     * 一个参数: checkout [branch] 切换分支
     * 两个参数: checkout -- [file name]
     * 三个参数: checkout [commit id] -- [file name]
     *
     * @param args 参数应为"checkout [branch]"或"checkout -- [file name]"或"checkout
     *             [commit id] -- [file name]"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void checkout(String[] args) {
        // 不同个数的参数
        if (args.length == 2) {
            // checkout [branch]
            checkout(args[1]);
        } else if (args.length == 3) {
            // checkout -- [file name]
            if (!args[1].equals("--")) {
                System.out.println("符号使用错误！");
                return;
            }
            String fileName = args[2];
            checkout(getBranchHead(currentBranch, 1, ""), fileName);
        } else if (args.length == 4) {
            // checkout [commit id] -- [file name]
            if (!args[2].equals("--")) {
                System.out.println("符号使用错误！");
                return;
            }
            String commitID = args[1];
            String fileName = args[3];
            checkout(commitID, fileName);
        } else {
            System.out.println("参数数量错误！");
        }
    }

    /**
     * checkout一个分支
     * 先判断，如果是当前分支，那么输出：No need to checkout the current branch.
     * 再判断，分支是否存在，不存在，输出: No such branch exists.
     *
     * @param branch 目标分支
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void checkout(String branch) {
        String[] temp = branch.split("/");
        int type = temp.length;

        if (branch.equals(currentBranch)) {
            System.out.println("已处于该分支！ ");
            return;
        }
        if (type == 1 && !hasBranch(branch, 1, "")) {
            System.out.println("不存在此分支！");
            return; // 没有这个branch
        }

        if (type == 2 && !hasBranch(temp[1], 2, temp[0])) {
            System.out.println("不存在此分支！");
            return; // 没有这个branch
        }

        String commitID;
        if (type == 1)
            commitID = getBranchHead(branch, 1, ""); // new branch的HEAD指向的commitId
        else
            commitID = getBranchHead(temp[1], 2, temp[0]); // new branch的HEAD指向的commitId

        if (!changeCommit(currentCommit, commitID))
            return;

        if (type == 1) {
            writeContents(HEAD_FILE, "refs/heads/" + branch);
        } else {
            writeContents(HEAD_FILE, "refs/remotes/" + branch);
        }
        currentBranchPath = Utils.readContentsAsString(HEAD_FILE);
        currentBranch = branch;
        currentCommit = commitID;
        recordOperation("checkout " + branch);
    }

    /**
     * 查看commitID号commit中的fileName文件
     * 如果输入的是开头几个字符，遍历所有CommitId，补全ID,
     * 如果COMMIT_DIR+FileName不存在 或 该Commit没有此File,输出： File does not
     * exist in that commit.
     * 最后把blob中的内容写到工作区的file中去
     *
     * @param commitID 某次ID
     * @param fileName 哪个文件名
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void checkout(String commitID, String fileName) {
        // 如果使用的是uid（比较短的id）需要找到原始的id
        if (commitID.length() < 40) {
            List<String> fileList = plainFilenamesIn(COMMITS_DIR);
            if (fileList != null) {
                for (String tmp : fileList) {
                    if (tmp.startsWith(commitID)) {
                        commitID = tmp;
                        break;
                    }
                }
            }
        }
        File f = join(COMMITS_DIR, commitID);

        if (!f.exists()) {
            System.out.println("没有此次提交ID记录!");
            return;
        }

        Commit commit = Commit.fromFile(COMMITS_DIR, commitID);

        if (!commit.hasFile(fileName)) { // 没有这个文件
            System.out.println("此次提交中不存在此文件！");
            return;
        }
        writeBlobToFile(fileName, commit.getBlob(fileName));
        recordOperation("checkout " + commitID + " " + fileName);
    }

    /**
     * 获得branch分支的头commit
     *
     * @param branch 分支名
     * @param type   1/本地；2/远程
     * @param remote 远程仓库名
     * @return fileContent 提交的ID值
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public String getBranchHead(String branch, int type, String remote) {
        File file;
        if (type == 1) {
            file = join(BRANCH_HEADS_DIR, branch);
        } else {
            file = join(REMOTE_HEADS_DIR, remote, branch);
        }
        return readContentsAsString(file);
    }

    /**
     * 判断是否存在branch分支
     *
     * @param branch 分支
     * @param type   有无remote(1/无，2/有
     * @param remote 远程仓库名
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public boolean hasBranch(String branch, int type, String remote) {
        File file;
        if (type == 1) {
            file = join(BRANCH_HEADS_DIR, branch);
        } else {
            file = join(REMOTE_HEADS_DIR, remote, branch);
        }
        return file.exists();
    }

    /**
     * 从第一个commit的状态切换为第二个commit的状态
     * 修改文件+改变HEAD对应文件的内容
     * 如果存在untracked文件，输出：There is an untracked file in the way; delete it, or add
     * and commit it first.
     * 遍历Commit1和Commit2所有文件，如果是C1独有文件，删除
     * 清空INDEX_DIR
     *
     * @param commitID1 当前提交ID
     * @param commitID2 将要替换的提交ID
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public boolean changeCommit(String commitID1, String commitID2) {
        Commit commit1 = Commit.fromFile(COMMITS_DIR, commitID1);
        Commit commit2 = Commit.fromFile(COMMITS_DIR, commitID2);

        // 在另一个分支中有这个文件，在这个分支中被修改了但是没有add也没有commit
        // 在当前working directory是否有修改（是否和上个commit不同）
        Set<String> files1 = commit1.getFiles();
        Set<String> files2 = commit2.getFiles();
        Set<String> allFiles = new HashSet<>();
        allFiles.addAll(files1);
        allFiles.addAll(files2);
        String m = "当前仍有未追踪文件！请先删除或添加，然后提交！";
        if (hasUntracked(commitID1, commitID2)) {
            System.out.println(m);
            return false;
        }

        for (String fileName : allFiles) {
            if (files1.contains(fileName) && !files2.contains(fileName)) {
                File f = join(CWD, fileName);
                if (f.exists()) {
                    f.delete();
                }
            } else {
                writeBlobToFile(fileName, commit2.getBlob(fileName));
            }
        }
        currentStage = new Stage();
        currentStage.saveStage(INDEX_FILE);
        return true;
    }

    /**
     * 把blob中的内容写到file中去，是安全的，会判断是否有这个文件
     *
     * @param fileName 文件名
     * @param blobHash 文件内容的哈希值
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void writeBlobToFile(String fileName, String blobHash) {
        File f = join(CWD, fileName);
        writeContents(f, Blob.getContentFromFile(BLOBS_DIR, blobHash));
    }

    /**
     * 遍历工作区所有文件，若currentCommit和currentStage均没有，或者currentStage removed文件，那么添加到返回列表
     *
     * @return untrackedFileList 未被追踪的文件列表
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public Set<String> getUntrackedFiles() {
        Commit c = Commit.fromFile(COMMITS_DIR, currentCommit);
        List<String> curFiles = plainFilenamesIn(CWD);
        Set<String> files = new TreeSet<>();

        if (curFiles != null) {
            for (String file : curFiles) {
                if (!c.hasFile(file) && !currentStage.hasFile(file)) {
                    files.add(file);
                } else if (currentStage.hasRemoved(file)) {
                    files.add(file);
                }
            }
        }
        return files;
    }

    /**
     * 如果：上次提交了，现在修改了，没add，modified;已经add了，但是又修改了,modified；已经add了,但是删除了，deleted;commit里面有，当前没有，当前removeFile也没有，deleted
     *
     * @return files 更改了的文件的列表
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public Set<String> getModifiedFiles() {
        Set<String> files = new HashSet<>();
        List<String> curFiles = plainFilenamesIn(CWD);
        Commit c = Commit.fromFile(COMMITS_DIR, currentCommit);
        Set<String> trackedFiles = c.getFiles();

        if (curFiles != null) {
            for (String file : curFiles) {
                if (!currentStage.hasFile(file)) {
                    // case 1:
                    if (c.hasFile(file)) {
                        Blob b = Blob.fromFile(BLOBS_DIR, c.getBlob(file));
                        String content1 = readContentsAsString(join(CWD, file));
                        if (!content1.equals(b.getContent())) {
                            files.add(file + " (已变动)");
                        }
                    }
                } else {
                    // case 2:
                    String content1 = readContentsAsString(join(CWD, file));
                    Blob b = Blob.fromFile(BLOBS_DIR, currentStage.getBlob(file));
                    if (!content1.equals(b.getContent())) {
                        files.add(file + " (已变动)");
                    }
                }
            }
        }
        Set<String> addFiles = currentStage.getFiles();
        Set<String> removeFiles = currentStage.getRemovedFiles();

        for (String file : addFiles)
            if (curFiles != null && !curFiles.contains(file)) files.add(file + " (已删除)");
        for (String file : trackedFiles)
            if (curFiles != null && !curFiles.contains(file) && !removeFiles.contains(file))
                files.add(file + " (已删除)");

        return files;
    }

    /**
     * 判断当前有无未追踪文件，(变更提交前查看)
     * 先遍历c2文件，若c1没有，但是工作区有，返回true
     * 再遍历c1文件，若c2没有，但是工作区有，若c1文件和工作区文件sha1不等，那么返回true
     *
     * @param cid1 当前提交
     * @param cid2 要变更到的提交
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    private boolean hasUntracked(String cid1, String cid2) {
        Commit c1 = Commit.fromFile(COMMITS_DIR, cid1);
        Commit c2 = Commit.fromFile(COMMITS_DIR, cid2);
        List<String> files = plainFilenamesIn(CWD);

        for (String f : c2.getFiles()) {
            if (files != null && !c1.hasFile(f) && files.contains(f)) {
                return true;
            }
        }
        for (String f : c1.getFiles()) {
            if (files != null && !c2.hasFile(f) && files.contains(f)) {
                String curFile = sha1(readContents(join(CWD, f)));
                if (!c1.getBlob(f).equals(curFile)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 移除一个branch-删除HEAD_DIR+branch文件。
     * 如果是当前branch,输出：Cannot remove the current branch. ；如果不存在此branch，输出A branch
     * with that name does not exist.；
     *
     * @param branch 分支名
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void removeBranch(String branch) {
        if (branch.equals(currentBranch)) {
            System.out.println("无法移除当前分支！");
            return;
        }
        if (!hasBranch(branch, 1, "")) {
            System.out.println("不存在同名分支！");
            return; // 没有这个branch
        }
        File file = join(BRANCH_HEADS_DIR, branch);
        file.delete();
        recordOperation("removeBranch " + branch);
    }

    /**
     * 移除分支
     *
     * @param args 参数形如"rm-branch [BranchName]"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void removeBranch(String[] args) {
        if (args.length > 2) {
            System.out.println("参数数量错误！");
            return;
        }
        removeBranch(args[1]);
    }

    /**
     * 回滚到之前提交，如果文件不存在，输出:No commit with that id exists.
     *
     * @param commitID 目标提交ID
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void reset(String commitID) {
        File file = join(COMMITS_DIR, commitID);

        if (!file.exists()) {
            System.out.println("没有此次提交ID记录!");
            return;
        }
        // 如果有untracked等情况会在这个函数中处理
        changeCommit(currentCommit, commitID);
        writeRefs(currentBranch, commitID);
        currentCommit = commitID;
        recordOperation("reset " + commitID);
    }

    /**
     * 回滚到之前提交
     *
     * @param args 参数形如"reset [CommitID]"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void reset(String[] args) {
        if (args.length > 2) {
            System.out.println("参数数量错误！");
            return;
        }
        reset(args[1]);
    }

    /**
     * 新建一条commit，HEAD指针往后移动到这个commit，默认parent1ID为当前commitId,合并的时候，parent2ID为另一个branch。
     * 把stage里面的blob都加进去，把要remove的blob都remove再保存一下commit。
     * 如果message为空，输出：Please enter a commit message.；
     * 如果parent2为空，同时staging area为空，输出：No changes added to the commit.
     *
     * @param message 提交时附带的信息
     * @param parent2 合并分支时另一个父提交
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void commit(String message, String parent2) {
        if (currentStage.empty() && parent2.isEmpty()) {
            // staging area为空
            JOptionPane.showMessageDialog(null, "发生错误: " + "must be a normal file", "警告", JOptionPane.WARNING_MESSAGE);
            System.out.println("Commit未发生变化!");
            // No changes added to the commit.
            return;
        }
        Commit commit = new Commit(COMMITS_DIR, message, currentCommit, parent2);
        commit.add(currentStage.getFileToBlob());
        // 把stage里面的blob都加进去
        commit.remove(currentStage.getRemovedFiles());
        // 把要remove的blob都remove
        currentCommit = commit.getHash();
        commit.saveCommit(COMMITS_DIR);
        writeRefs(currentBranch, currentCommit);
        currentStage = new Stage();
        currentStage.saveStage(INDEX_FILE);
        System.out.println("success!");
        recordOperation("commit " + message);
    }

    /**
     * 新建一条commit
     *
     * @param cmd 参数形如"commit [Message]"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void commit(String[] cmd) {
        if (cmd.length == 1) {
            System.out.println("请输入提交信息！");
            return;
        }
        if (cmd.length > 2) {
            System.out.println("参数数量错误！");
            return;
        }
        commit(cmd[1], "");
    }

    /**
     * 新建一个名为branch的分支，把currentCommit写入新branch
     * 如果已经存在名字相同分支，那么输出：A branch with that name already exists.
     *
     * @param branch 分支名
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void branch(String branch) { // 新建一个分支
        File file = join(BRANCH_HEADS_DIR, branch);
        if (file.exists()) { // 已经存在这个分支
            System.out.println("已经存在同名分支！");
            return;
        }
        writeContents(file, currentCommit);
        recordOperation("branch " + branch);
    }

    /**
     * 新建分支
     *
     * @param args 参数形如"branch [BranchName]"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void branch(String[] args) {
        if (args.length != 2) {
            System.out.println("参数数量错误！");
            return;
        }
        branch(args[1]);
    }

    /**
     * 对应git add操作
     * 文件不存在,输出：File does not exist.
     * 判断这个文件和当前commit中的版本是否相同，如果相同就从staging area把它请出来，把Removed也清掉
     * 否则生成新的blob，然后在stage中加入/修改 fileName:blobHash
     *
     * @param fileName 文件名
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void add(String fileName) {
        File file = join(CWD, fileName);
        if (!file.exists()) {
            // 如果文件不存在就报错
            System.out.println("不存在此文件！");
            return;
        }

        Commit c = Commit.fromFile(COMMITS_DIR, currentCommit);
        byte[] blob = readContents(file);
        // 存放文件内容
        String blobHash = sha1(readContents(file));
        // 存放文件内容哈希
        if (c.hasFile(fileName) && c.getBlob(fileName).equals(blobHash)) {
            if (currentStage.empty()) {
                return;
            }
            currentStage.removeAdd(fileName);
            // 移走关于这个fileName的add项
            currentStage.removeRm(fileName);
            // 移走关于这个fileName的remove项
            currentStage.saveStage(INDEX_FILE);
            return;
        }
        Blob b = new Blob(blobHash, blob);
        b.saveBlob(BLOBS_DIR);
        currentStage.add(fileName, blobHash);
        currentStage.saveStage(INDEX_FILE);
        recordOperation("add " + fileName);
    }

    /**
     * 添加文件进暂存区
     *
     * @param cmd 参数形如"add [FileName]"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void add(String[] cmd) {
        if (cmd.length != 2) {
            System.out.println("参数数量错误！");
            return;
        }
        add(cmd[1]);
    }

    /**
     * 对应git rm -f 操作
     * 1.在staging area中删除这个文件
     * 2.在CWD中删除这个文件
     * 3.在stage文件对应的removedFiles里面加入这个文件(前提是在当前commit被追踪)
     *
     * @param fileName 文件名
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void remove(String fileName) {
        // 判断这个文件是否在当前文件中被跟踪
        Commit commit = Commit.fromFile(COMMITS_DIR, currentCommit);
        if (!commit.hasFile(fileName) && !currentStage.hasFile(fileName)) {
            // stage中没有加入该文件，在之前的commit中也没有这个文件
            System.out.println("无法移除此文件！");
            return;
        }
        if (currentStage.hasFile(fileName))
            currentStage.removeAdd(fileName);
        // 如果添加到了暂存区 从暂存区删掉
        // 如果上一个commit中追踪了这个文件，就添加到removed列表
        if (commit.hasFile(fileName)) {
            currentStage.addRm(fileName);
            // 如果之前都有这个文件 才需要删掉这个文件并加入removed
            File file = join(CWD, fileName);
            restrictedDelete(file);
        }
        currentStage.saveStage(INDEX_FILE);
        recordOperation("remove " + fileName);
    }

    /**
     * 删除暂存区中文件
     *
     * @param cmd 参数形如"remove [FileName]"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void remove(String[] cmd) {
        if (cmd.length != 2) {
            System.out.println("参数数量错误！");
            return;
        }
        remove(cmd[1]);
    }

    /**
     * 打印status信息
     *
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void status() {
        showBranches();
        System.out.println();
        showStaged();
        System.out.println();
        showRemoved();
        System.out.println();
        showModifiedButNotStaged();
        System.out.println();
        showUntracked();
        System.out.println();
    }

    public void status(String[] cmd) {
        if (cmd.length > 1) {
            System.out.println("参数数量错误！");
            return;
        }
        status();
    }

    /**
     * 帮助status的函数
     * 升序输出所有分支，如果是当前分支，前面加一个*
     *
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void showBranches() {
        System.out.println("=== Branches ===");
        String[] branches = BRANCH_HEADS_DIR.list();
        if (branches != null) {
            Arrays.sort(branches);
            for (String b : branches) {
                if (currentBranch.equals(b)) {
                    System.out.println("*" + b);
                } else {
                    System.out.println(b);
                }
            }
        }
    }

    /**
     * 帮助status的函数
     * 无序遍历输出currentStage keySet
     * 展示所有已暂存的文件
     *
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void showStaged() {
        System.out.println("=== Staged Files ===");
        TreeMap<String, String> map = currentStage.getFileToBlob();
        for (String file : map.keySet()) {
            System.out.println(file);
        }
    }

    /**
     * 帮助status的函数
     * 遍历输出currentStage.RemovedDile
     *
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void showRemoved() {
        System.out.println("=== Removed Files ===");

        Set<String> removed = currentStage.getRemovedFiles();
        for (String file : removed) {
            System.out.println(file);
        }
    }

    /**
     * 遍历输出已修改/删除文件名
     *
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void showModifiedButNotStaged() {
        System.out.println("=== Modifications Not Staged For Commit ===");
        Set<String> files = getModifiedFiles();
        for (String file : files)
            System.out.println(file);
    }

    /**
     * 输出所有untracked文件
     *
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void showUntracked() {
        System.out.println("=== Untracked Files ===");
        Set<String> files = getUntrackedFiles();
        for (String file : files) {
            System.out.println(file);
        }
    }

    /**
     * 对标merge-base功能
     * 找到两个分支commit的最近公共祖先/split point
     * 先让远的先网上溯源几次，相同时，再同时向上溯源
     *
     * @param commitID1 第一个ID
     * @param commitID2 第二个ID
     * @return 公共祖先的hash
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public String findCommonAncestor(String commitID1, String commitID2) {
        int length1 = commitLength(commitID1);
        int length2 = commitLength(commitID2);

        Commit commit1 = Commit.fromFile(COMMITS_DIR, commitID1);
        Commit commit2 = Commit.fromFile(COMMITS_DIR, commitID2);

        // 处理两个parent的情况
        if (!commit1.getParent2().isEmpty()) {
            return findCommonAncestor(commit1.getParent2(), commitID2);
        }

        String parent1;

        // 让length1>=length2,这样commit1永远在commit2前面
        if (length2 > length1) {
            Commit temp = commit1;
            commit1 = commit2;
            commit2 = temp;
        }

        for (int i = 0; i < Math.abs(length1 - length2); i++) {
            parent1 = commit1.getParent();
            commit1 = Commit.fromFile(COMMITS_DIR, parent1);
        }

        while (!commit1.getHash().equals(commit2.getHash())) {
            commit1 = Commit.fromFile(COMMITS_DIR, commit1.getParent());
            commit2 = Commit.fromFile(COMMITS_DIR, commit2.getParent());
        }

        return commit1.getHash();
    }

    /**
     * 克隆远程仓库
     *
     * @param remoteName 远程仓库名
     * @throws IOException 如果发生I/O异常
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void Clone(String remoteName) throws IOException {
        if (Objects.requireNonNull(COMMITS_DIR.list()).length != 1 || Objects.requireNonNull(CWD.list()).length != 1) {
            System.out.println("请选择空仓库进行克隆");
            return;
        }
        String[] allRemoteBranch = getAllBranchFromRemoteName(remoteName);
        for (String branchName : allRemoteBranch) {
            fetch(remoteName, branchName);
        }
    }

    public void Clone(String[] cmd) throws IOException {
        if (cmd.length != 2) {
            System.out.println("参数数量错误！");
            return;
        }
        Clone(cmd[1]);
    }

    /**
     * 获取远程仓库的所有分支
     *
     * @param remoteName 远程仓库名
     * @return 远程仓库的所有分支名
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public String[] getAllBranchFromRemoteName(String remoteName) {
        String url = getUrlFromRemoteName(remoteName);
        if (url == null) {
            System.out.printf("%s远程仓库未添加!", remoteName);
            return null;
        }
        File f = new File(url);
        if (!f.exists()) {
            System.out.println("远程地址不存在!");
            return null;
        }
        File remoteBranch = join(url, "heads");
        if (!remoteBranch.exists()) {
            System.out.println("远程仓库出错");
            return null;
        }
        return remoteBranch.list();
    }

    /**
     * commit到initial commit的距离（如果加上initial commit是第五个就返回5）
     *
     * @return commit到initial commit的距离（如果加上initial commit是第五个就返回5）
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public int commitLength(String commitID) {
        int length = 0;
        Commit commit = Commit.fromFile(COMMITS_DIR, commitID);
        String curCommit = commit.getParent();
        length++;
        while (!curCommit.isEmpty()) {
            commit = Commit.fromFile(COMMITS_DIR, curCommit);
            curCommit = commit.getParent();
            length++;
        }
        return length;
    }

    /**
     * 查看commit中文件存在及相互冲突情况
     *
     * @param cid1     提交1
     * @param cid2     提交2
     * @param fileName 文件名
     * @return 0表示在两个commit内容一致 1表示在两个commit内容不同
     * 2表示在两个commit都不存在 3表示在commit1存在 在commit2中不存在
     * 4表示在commit2存在 在commit1中不存在
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public int modCode(String cid1, String cid2, String fileName) {
        Commit c1 = Commit.fromFile(COMMITS_DIR, cid1);
        Commit c2 = Commit.fromFile(COMMITS_DIR, cid2);
        boolean h1 = c1.hasFile(fileName);
        boolean h2 = c2.hasFile(fileName);
        if (!h1 && !h2) {
            return 2; // 都不在
        } else if (h1 && !h2) {
            return 3; // 删除
        } else if (!h1) {
            return 4; // 添加文件
        } else {
            if (c1.getBlob(fileName).equals(c2.getBlob(fileName))) {
                return 0; // 都在且内容一样
            }
            return 1; // 都在但是内容不同
        }
    }

    /**
     * 根据前面的modCode来判断当前分支、合并分支和祖先分支中该文件关系
     *
     * @param sid 祖先分支
     * @param cid 当前分支
     * @param bid 另一个分支
     * @param f   查找文件
     * @return 返回的值对应给出的详细功能中的case 1-8
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public int judgeCondition(String sid, String cid, String bid, String f) {
        int code1 = modCode(sid, cid, f);
        int code2 = modCode(sid, bid, f);
        int code3 = modCode(cid, bid, f);

        if (code1 == 0 && code2 == 0) { // 都毫无修改
            return 0;
        } else if (code1 == 0 && code2 == 1 && code3 == 1) { // 只有other修改
            return 1;
        } else if (code1 == 1 && code2 == 0 && code3 == 1) { // 只有当前修改
            return 2;
        } else if (code1 == 1 && code2 == 1 && code3 == 0) { // 都改了但是目前一样
            return 3;
        } else if (code1 == 3 && code2 == 3) {
            return 3;
        } else if (code1 == 4 && code2 == 4 && code3 == 0) {
            return 3;
        } else if (code1 == 4 && code2 == 2) {
            return 4;
        } else if (code1 == 2 && code2 == 4 && code3 == 4) {
            return 5;
        } else if (code1 == 0 && code2 == 3) {
            return 6;
        } else if (code1 == 3 && code2 == 0) {
            return 7;
        } else if (code1 == 1 && code2 == 1 && code3 == 1) {
            return 8;
        } else if (code1 == 1 && code2 == 3) {
            return 8;
        } else if (code1 == 3 && code2 == 1) {
            return 8;
        } else if (code1 == 4 && code2 == 4 && code3 == 1) {
            return 8;
        }
        return -1;
    }

    /**
     * 合并当前分支和另一个分支
     *
     * @param branch 合并的分支
     * @param type   是否remote 1不是
     *               另一个分支不存在 输出：A branch with that name does not exist.
     *               是本地分支，而且和currentBranch一样，输出：Cannot merge a branch with itself.
     *               如果祖先结点就是当前分支，切换到另一个分支，输出Current branch fast-forwarded.
     *               如果所给分支是祖先分支，输出 Given branch is an ancestor of the current
     *               branch.
     *               比较三个分支所有文件，根据存在、修改情况，判断合并的方法
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void merge(String branch, int type) {
        String remote = "";

        if (type == 2) {
            String[] temp = branch.split("/");
            branch = temp[1];
            remote = temp[0];
        }

        if (!currentStage.empty()) {
            System.out.println("尚未提交新变化！");
            // You have uncommitted changes.
            return;
        }
        if (!hasBranch(branch, type, remote)) {
            System.out.println("不存在同名分支！");
            // A branch with that name does not exist."
            return;
        }
        if (type != 2 && branch.equals(currentBranch)) {
            System.out.println("不能和自己合并！");
            // Cannot merge a branch with itself.
            return;
        }

        String branchCommit = getBranchHead(branch, type, remote);
        String splitPoint = findCommonAncestor(currentCommit, branchCommit);

        if (splitPoint.equals(currentCommit)) { // 分支点=等于当前分支 切换到给定分支
            checkout(branch); // 切换到给定分支
            System.out.println("指定分支领先！");
            // Current branch fast-forwarded.
            return;
        }

        // 判断是否有untrackedFiles会被覆盖
        if (hasUntracked(currentCommit, branchCommit)) {
            // String m = "There is an untracked file in the way; delete it, or add and
            // commit it first.";
            String m = "当前仍有未追踪文件！请先删除或添加，然后提交！";
            System.out.println(m);
            return;
        }

        Set<String> allFiles = getAllFiles(currentCommit, branchCommit, splitPoint);

        boolean conflict = false;
        for (String f : allFiles) {
            // 根据八个规律进行判断
            int code = judgeCondition(splitPoint, currentCommit, branchCommit, f);
            switch (code) {
                // 用合并的文件，add
                case 1, 5 -> {
                    // 新建合并文件，add
                    writeCommitToFile(branchCommit, f);
                    add(f);
                }
                case 6 ->
                    // 删除文件，remove
                        remove(f);
                case 8 -> {
                    // 写冲突文件，add
                    conflict = true;
                    writeConflictFile(currentCommit, branchCommit, f);
                    add(f);
                }
                default -> {
                }
            }
        }
        if (conflict) {
            System.out.println("存在合并冲突！");
            // Encountered a merge conflict.
        }
        String msg = "Merged " + branch + " into " + currentBranch + ".";

        if (type == 2) {
            msg = "Merged " + remote + "/" + branch + " into " + currentBranch + ".";
        }

        commit(msg, branchCommit);

        if (splitPoint.equals(branchCommit)) {
            // 分支点=给定分支 不做任何处理
            System.out.println("所给分支是当前分支的祖先！");
            // Given branch is an ancestor of the current branch.
        }
        recordOperation("merge " + branch + " " + type);
    }

    /**
     * 合并分支
     *
     * @param args 参数形如"merge [BranchName]"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void merge(String[] args) {
        if (args.length != 2) {
            System.out.println("参数数量错误！");
            return;
        }
        merge(args[1], 1);
    }

    /**
     * 所有文件并集的set集合
     *
     * @param cid 提交1的ID
     * @param bid 提交2的ID
     * @param sid 提交3的ID
     * @return 所有文件并集的set集合
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    private Set<String> getAllFiles(String cid, String bid, String sid) {
        Commit bCommit = Commit.fromFile(COMMITS_DIR, bid);
        Commit cCommit = Commit.fromFile(COMMITS_DIR, cid);
        Commit sCommit = Commit.fromFile(COMMITS_DIR, sid);
        Set<String> bFiles = bCommit.getFiles();
        Set<String> cFiles = cCommit.getFiles();
        Set<String> sFiles = sCommit.getFiles();
        Set<String> allFiles = new HashSet<>();
        allFiles.addAll(bFiles);
        allFiles.addAll(cFiles);
        allFiles.addAll(sFiles);
        return allFiles;
    }

    /**
     * 写入冲突文件
     * 读取两分支文件（可能为空），并把文件内容写到CWD的同名文件内
     *
     * @param cid1     提交1的ID
     * @param cid2     提交2的ID
     * @param fileName 发生冲突的文件
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    private void writeConflictFile(String cid1, String cid2, String fileName) {
        Commit c1 = Commit.fromFile(COMMITS_DIR, cid1);
        Commit c2 = Commit.fromFile(COMMITS_DIR, cid2);
        String blob1 = null;
        String blob2 = null;
        if (c1.hasFile(fileName)) {
            blob1 = c1.getBlob(fileName);
        }
        if (c2.hasFile(fileName)) {
            blob2 = c2.getBlob(fileName);
        }

        String res = "<<<<<<< HEAD\n";
        if (blob1 != null && !blob1.isEmpty()) {
            res += Blob.fromFile(BLOBS_DIR, blob1).getContent();
        }
        res += "=======\n";

        if (blob2 != null && !blob2.isEmpty()) {
            res += Blob.fromFile(BLOBS_DIR, blob2).getContent();
        }
        res += ">>>>>>>\n";

        File f = join(CWD, fileName);
        writeContents(f, res);
    }

    /**
     * 将提交写入文件
     *
     * @param cid      提交的ID
     * @param fileName 要写入的文件名
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    private void writeCommitToFile(String cid, String fileName) {
        Commit c = Commit.fromFile(COMMITS_DIR, cid);
        writeBlobToFile(fileName, c.getBlob(fileName));
    }

    /**
     * 退出程序
     *
     * @param args 参数应为"quit"
     * @author 王子琪、杨凯伟
     * @date 2023/11/24
     */
    public void quit(String[] args) {
        if (args.length != 1) {
            System.out.println("参数数量错误！");
            return;
        }
        recordOperation("quit");
        System.exit(0);
    }

    /**
     * 记录操作日志
     *
     * @param operation 进行的操作
     * @author 王子琪、杨凯伟、陈恒康
     * @date 2023/12/11
     */
    public void recordOperation(String operation) {
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        StringBuilder record = new StringBuilder(fmt.format(new Date()));
        record.append(" : ").append(operation).append("\n");
        File file = join(GITLET_DIR, "OPRecord");
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, true))) {
            writer.append(record.toString());
            writer.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 返回所有提交的信息
     *
     * @return list 所有提交的信息的列表
     * @author 王子琪、杨凯伟、陈恒康
     * @date 2023/12/11
     */
    public List<String> getAllCommitMsgId() {
        List<String> list = new ArrayList<>();
        Commit commit = Commit.fromFile(COMMITS_DIR, currentCommit);
        list.add(commit.getMessage() + " : " + commit.getHash());
        String curCommit = commit.getParent();
        while (!curCommit.isEmpty()) {
            commit = Commit.fromFile(COMMITS_DIR, curCommit);
            list.add(commit.getMessage() + " : " + commit.getHash());
            curCommit = commit.getParent();
        }
        return list;
    }
}
